<?php
// @formatter:off
/**
 * @file WCRepresentation.abs.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: WCRepresentation.abs.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.1/includes/representations/WCRepresentation.abs.php $
 */
// @formatter:on

/**
 * @abstract
 * @class WCRepresentation This class is an abstract representation of database
 * table rows. Basically, it holds the logic to treat a row columns as public
 * variables of an object. For example, given the next table:
 * @code
 *	create table my_table ()
 *		myt_id		int not null primary key auto_incremet,
 *		myt_name	varchar(20) not null,
 *		myt_somevalue	int not null default '0'
 *	);
 * @endcode
 * A representation can be access in this way:
 * @code
 *	echo "{$item->name} ({$item->id})\n";
 *	$item->somevalue = 15;
 * @endcode
 */
abstract class WCRepresentation {
	/**
	 * @var string[] This variable holds the list of columns that cannot be
	 * directly modified by the pseudo-method __set().
	 * By default, this list only contains 'id' which represents any column
	 * named as '%id', where '%' is given by '$this->_PropertiesPrefix'.
	 */
	protected $_PropertiesReadOnly = array("id");
	/**
	 * @var string[] This variable holds the list of columns that must be
	 * treated as booleans and then send to the database as 'Y' and 'N' by
	 * the pseudo-method __set().
	 * By default, this list is empty.
	 */
	protected $_PropertiesBoolean = array();
	/**
	 * @var string This is the prefix of every column on the represented
	 * table.
	 */
	protected $_PropertiesPrefix = "";
	/**
	 * @var string This is the name of the represented table.
	 */
	protected $_PropertiesTable = "";
	/**
	 * @var string This variable holds the name of the column for the
	 * represented item's name.
	 * By default, it is 'name'.
	 */
	protected $_PropertiesNameColumn = "name";
	/**
	 * @var string This is of query that will be used when a row of the
	 * represented table is updated.
	 * It may by something like:
	 * 	', skp_timestamp = sysdate()"'
	 * @note the comma at the beginning is important.
	 */
	protected $_PropertiesAutoSet = "";
	/**
	 * @var string[] This variable holds the list of columns that may hold a
	 * null value. This is list is used by the pseudo-method __set() to
	 * perform the right operation when it's null.
	 * By default, this list is empty.
	 */
	protected $_PropertiesNULL = array();
	/**
	 * @var string[string] This associative array holds a list of function
	 * names to be applied on certain column when calling the the
	 * pseudo-method __set(). Such function will affect the value just before
	 * it is impacted on table.
	 * The key of this list is the name of a column (without prefix) and the
	 * value is the name of a function with the next form:
	 * @code
	 * 	function func($value) {
	 * 		// ... some logic ...
	 * 		return $newvalue;
	 * 	}
	 * @endcode
	 * By default, it's empty.
	 */
	protected $_PropertiesPresetFunc = array();
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	/**
	 * @var mixed[string] This is the information retrieved from database and
	 * through it represent an item. It used by pseudo-method __get() to avoid
	 * checking database on each access and also by __set() to perform its
	 * formulas before updating database.
	 */
	protected $_info = array();
	/**
	 * @var boolean This flag indicates if the represented item had issues
	 * when it was loaded or not.
	 */
	protected $_ok;
	/**
	 * Class constructor.
	 */
	public function __construct() {
		//
		// Loading global pointers.
		global $wcDatabaseAccess;
		//
		// Not being loaded is considered to be an issue.
		$this->_ok = false;
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess['prefix'];
		// @}
	}
	//
	// Public methods.
	/**
	 * This pseudo-method gives an easy way to access a represented item's
	 * data using column names (without prefixes).
	 *
	 * @param string $name Name of a column (without prefixes).
	 * @return mixed Returns column's value.
	 */
	public function __get($name) {
		//
		// Building column's real name.
		$property = $this->_PropertiesPrefix.$name;
		//
		// If the column seems to not be set, it may be a null column.
		if(!isset($this->_info[$property]) && in_array($name, $this->_PropertiesNULL)) {
			$this->_info[$property] = false;
		}
		//
		// Checking if the column is present on the table and returning
		// it. In other words, if it's on the loaded list.
		return isset($this->_info[$property]) ? $this->_info[$property] : false;
	}
	/**
	 * This pseudo-method gives an easy way to modify database columns for a
	 * specific row represented by this object. For example, given a table
	 * with columns named col_% (set by $_PropertiesPrefix), something like
	 * this can be done:
	 * @code
	 * $object->color = "red";
	 * $object->width = 10;
	 * @endcode
	 * This will mean to set column col_color to "red" and column col_width to
	 * 10.
	 *
	 * @param string $name Property name (without prefix).
	 * @param string $value Value to be set.
	 * @return boolean Returns true when the value was successfully set in the
	 * database.
	 */
	public function __set($name, $value) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting default value to be returned.
		$out = false;
		//
		// Checking that:
		//	- Read Only Mode is not activated.
		//	- The current object has no issues.
		//	- And the property to be edited is not a read only property.
		if(!$wcDefaults["read-only"] && $this->ok() && !in_array($name, $this->_PropertiesReadOnly)) {
			//
			// Building the real property name (table column name).
			$property = $this->_PropertiesPrefix.$name;
			//
			// If the property is not set in the current properties
			// list and it can be NULL, it is internally set as false.
			if(!isset($this->_info[$property]) && in_array($name, $this->_PropertiesNULL)) {
				$this->_info[$property] = false;
			}
			//
			// Checking if it's a valid property.
			if(isset($this->_info[$property])) {
				//
				// Creating a specific statement to update this
				// property.
				// @{
				$stmt = false;
				$stmtID = __CLASS__."::".__FUNCTION__."[{$property}]";
				if($this->_db->has($stmtID)) {
					$stmt = $this->_db->get($stmtID);
				} else {
					$prefix = $this->_PropertiesPrefix;
					$table = $this->_dbprefix.$this->_PropertiesTable;

					$query = "update  {$table}\n";
					$query .= "set     {$property} = :value{$this->_PropertiesAutoSet}\n";
					$query .= "where   {$prefix}id = :id\n";
					$stmt = $this->_db->prepare($stmtID, $query);
				}
				// @}
				//
				// $value may not be appropriated for the database,
				// therefore $realValue will be built and send.
				$realValue = $value;
				//
				// If it is boolean property it must be either 'Y'
				// or 'N' (for true and false).
				if(in_array($name, $this->_PropertiesBoolean)) {
					$realValue = $value ? "Y" : "N";
				}
				//
				// If the property has an associated function that
				// manages it, it is used.
				if(isset($this->_PropertiesPresetFunc[$name])) {
					eval("\$realValue = {$this->_PropertiesPresetFunc[$name]}(\$realValue);");
				}
				//
				// Checking final settings.
				if($this->subSet($name, $value, $realValue)) {
					//
					// Building database parameters.
					$params = array(
						":value" => $realValue,
						":id" => $this->id
					);
					//
					// Attempting to modify the property in
					// the database.
					if($stmt->execute($params)) {
						//
						// If there are no errors, cached
						// property is modified.
						$this->_info[$property] = $value;
						//
						// At this point, property was
						// successfully set.
						$out = true;
					}
				}
			} else {
				//
				// This point is reached when an asked property
				// was not found on the database at loading.
				trigger_error(__CLASS__.": Property '{$name}' does not exists", E_USER_WARNING);
			}
		} elseif(!$this->ok()) {
			//
			// This point is reached when there's something wrong with
			// the current object, or when it's not yet loaded.
			trigger_error(__CLASS__.": Object is not loaded", E_USER_WARNING);
			//	} elseif($wcDefaults["read-only"]) {
			//		//
			//		// For other cases, except Read Only Mode, this will be
			//		// the exception thrown.
			//		trigger_error(__CLASS__.": Property '{$name}' cannot be modified",
			// E_USER_WARNING);
		}
		//
		// Returning modification status.
		return $out;
	}
	/**
	 * This method allows to know the represented item's ID.
	 *
	 * @return int Returns item's ID.
	 */
	public function id() {
		return $this->id;
	}
	/**
	 * This method allows to know the represented item's name.
	 *
	 * @return string Returns item's name.
	 */
	public function name() {
		eval("\$name = \$this->{$this->_PropertiesNameColumn};");
		return $name;
	}
	/**
	 * This method allows to know if the represented item has issues.
	 *
	 * @return string Returns item's status.
	 */
	public function ok() {
		return $this->_ok;
	}
	/**
	 * @abstract
	 * This is an abstract method that allows a method to reload all its
	 * information from database.
	 *
	 * @return boolean Returns true when the object was successfully reloaded.
	 */
	public function reload() {
		return false;
	}
	/**
	 * This method remove the represented object from the database.
	 *
	 * @return boolean Returns true when it was successfully removed.
	 */
	public function remove() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// Creating a statement to remove this object.
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__."[".$this->_PropertiesTable."]";
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$prefix = $this->_PropertiesPrefix;
				$table = $this->_dbprefix.$this->_PropertiesTable;

				$query = "delete from {$table}\n";
				$query .= "where       {$prefix}id = :id\n";
				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			//
			// Building database parameters.
			$params = array(":id" => $this->id);
			//
			// Attempting to remove it.
			if($stmt->execute($params)) {
				//
				// At this point, the object was successfully
				// removed, basic values and returning value are
				// set.
				// @{
				$this->_ok = false;
				$this->_info = array();

				$out = true;
				// @}
			}
		}
		//
		// Returning removal result.
		return $out;
	}
	//
	// Protected methods.
	/**
	 * This is an internal method called by the pseudo-method __set() and it
	 * allows to perform some checks on values before they affect the
	 * information on database.
	 *
	 * @param string $name Name of the parameter being modified.
	 * @param string $value Value to set to the property with name $name.
	 * @param string $realValue Value built by the pseudo-method __set().
	 * @return boolean Returns true when it is alright to modify the property.
	 */
	protected function subSet($name, &$value, &$realValue) {
		//
		// By default, this method does nothing and always return true.
		return true;
	}
}

/**
 * @interface WCPreviews This interface specifies basic methods for item
 * representations where those items my have preview images.
 */
interface WCPreviews {
	/**
	 * This method creates a the previews list based on data from the object,
	 * and caches this data for further uses.
	 *
	 * @return boolean Returns true if a previews list was generated.
	 */
	public function generatePreviews();
	/**
	 * This method allows to know if the object has a main preview image.
	 *
	 * @return boolean Returns true when the object has a main preview image.
	 */
	public function hasPreview();
	/**
	 * This method allows to know if the object has a list of preview images.
	 *
	 * @return boolean Returns true when the object has a list of preview
	 * images.
	 */
	public function hasPreviews();
	/**
	 * This method allows to get the object's main preview image.
	 *
	 * @return string Returns a URI of the object's main preview image.
	 */
	public function preview();
	/**
	 * This method allows to get the object's list of preview images.
	 *
	 * @return string[] Returns a URI list of the object's preview images.
	 */
	public function previews();
}
?>